package uploader

import (
	"errors"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"shop-common/library/loggerV2"
	"shop-common/library/variables"
	"strconv"
	"strings"
	"time"
)

type LocalUploader struct{}

// 本地文件系统
type IUploader interface {
	Put(validFile *FileRequest) (*FileVO, error)
	Move(oldPath, newPath string) (string, error)
	BucketDelete(names ...string) error
}

func NewLocalUploader() IUploader {
	return &LocalUploader{}
}

/**
图片上传
*/
func (local *LocalUploader) Put(fileRequest *FileRequest) (*FileVO, error) {
	defaultFile := variables.ZoneSetting.Local.ImagePath
	if err := os.MkdirAll(defaultFile, os.ModeDir); err != nil {
		return nil, err
	}

	if fileRequest.AttDir == "" {
		fileRequest.AttDir = time.Now().Format("2006/01/02") + "/"
	}

	filename := path.Join(defaultFile, fileRequest.AttDir, fileRequest.NewFileName)

	if err := os.MkdirAll(filepath.Dir(filename), os.ModePerm); err != nil {
		return nil, err
	}

	if err := ioutil.WriteFile(filename, fileRequest.FileStream, os.ModePerm); err != nil {
		return nil, err
	}

	size := len(fileRequest.FileStream)
	ct := strings.Split(fileRequest.FileType, "/")

	return &FileVO{
		RealName:     fileRequest.RealName,
		Name:         fileRequest.NewFileName,
		AttDir:       fileRequest.AttDir,
		AttSize:      strconv.Itoa(size),
		AttType:      ct[len(ct)-1],
		ContentType:  fileRequest.FileType,
		LocationType: LOCAL_TYPE_ATTACHMENT,
		Key:          "",
		Hash:         "",
		Bucket:       "",
	}, nil
}

func (local *LocalUploader) Move(oldFilenames, newFilenames string) (string, error) {
	defaultFile := variables.ZoneSetting.Local.ImagePath
	if !isExist(defaultFile + oldFilenames) {
		return "", errors.New("file not exist")
	}

	return newFilenames, os.Rename(defaultFile+oldFilenames, defaultFile+newFilenames)
}

func (local *LocalUploader) BucketDelete(filenames ...string) error {
	defaultFile := variables.ZoneSetting.Local.ImagePath
	for _, filename := range filenames {
		if !isExist(defaultFile + filename) {
			break
			//return errors.New("file not exist")
		}
		os.Remove(defaultFile + filename)
	}

	return nil
}

func isExist(path string) bool {
	_, err := os.Stat(path)
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		if os.IsNotExist(err) {
			return false
		}
		return false
	}
	return true
}

func MergeChunks(request *ShardFileRequest) (*ShardFileResponse, error) {
	defaultFile := variables.ZoneSetting.Local.ImagePath
	var timeDir = strings.Replace(time.Now().Format("2006-01-02"), "-", "/", -1)
	finishDir := defaultFile + timeDir + "/"
	files, err := ioutil.ReadDir(path.Join(defaultFile, timeDir, request.Hash))
	if err != nil {
		loggerV2.Error(err)
		return nil, errors.New("切片合并失败")
	}

	toSize, err := DirSize(finishDir + request.Hash + "/")
	if err != nil {
		loggerV2.Error(err)
		return nil, errors.New("切片合并失败")
	}

	if toSize != int64(request.TotalSize) {
		return nil, errors.New("切片文件大小不正确")
	}

	fs, err := os.OpenFile(finishDir+request.Filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o644)
	defer fs.Close()
	if err != nil {
		loggerV2.Error(err)
		return nil, errors.New("创建合并文件失败")
	}

	for i := range files {
		content, _ := ioutil.ReadFile(path.Join(defaultFile, timeDir, request.Hash, request.Filename+"_"+strconv.Itoa(i+1)))
		_, err = fs.Write(content)

		if err != nil {
			if er := fs.Close(); er == nil {
				_ = os.Remove(finishDir + request.Filename)
			}
			loggerV2.Error(err)
			return nil, errors.New("文件合并失败")
		}
	}

	return &ShardFileResponse{
		Code: 2,
		Url:  strings.Join([]string{GetHostStatic(1), timeDir + "/" + request.Filename}, ""),
	}, os.RemoveAll(finishDir + request.Hash)
}

// 获取整体文件夹大小
func DirSize(path string) (int64, error) {
	var size int64
	err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			size += info.Size()
		}
		return err
	})
	return size, err
}
